using System;
using System.Collections.Generic;
using System.Xml;
using System.Text;
using Microsoft.Xna.Framework;

namespace SibLib
{
    /// <summary>
    /// Static helper to help deal with save games (saving/loading) in XML.
    /// </summary>
    public static class XmlSave
    {
        /// <summary>
        /// Current document being parsed.
        /// Is used by all saving methods.
        /// </summary>
        public static XmlDocument Document
        {
            get;
            set;
        }

        #region [SAVING] Adding attributes
        public static void AddAttribute(XmlNode node, string attrName, string value)
        {
            XmlAttribute attr = Document.CreateAttribute(attrName);
            attr.Value = value;
            node.Attributes.Append(attr);
        }

        /// <summary>
        /// Generic attribute add method.
        /// Checks for default value equality.
        /// </summary>
        /// <typeparam name="T">The type of the attribute, must be value type, comparable.</typeparam>
        /// <param name="node"></param>
        /// <param name="attrName"></param>
        /// <param name="value"></param>
        /// <param name="defaultValue"></param>
        public static void AddAttributeValue<T>(XmlNode node, string attrName, T value, T defaultValue) where T : struct, IComparable
        {
            // Is value equal to default?
            if (value.CompareTo(defaultValue) == 0)
                return;

            AddAttribute(node, attrName, value.ToString());
        }

        #region Aliases
        public static void AddAttributeInt(XmlNode node, string attrName, int value)
        {
            AddAttributeValue<int>(node, attrName, value, 0);
        }
        public static void AddAttributeInt(XmlNode node, string attrName, int value, int defaultValue)
        {
            AddAttributeValue<int>(node, attrName, value, defaultValue);
        }

        public static void AddAttributeFloat(XmlNode node, string attrName, float value)
        {
            AddAttributeValue<float>(node, attrName, value, 0.0f);
        }
        public static void AddAttributeFloat(XmlNode node, string attrName, float value, float defaultValue)
        {
            AddAttributeValue<float>(node, attrName, value, defaultValue);
        }

        public static void AddAttributeBool(XmlNode node, string attrName, bool value)
        {
            AddAttributeValue<bool>(node, attrName, value, false);
        }
        public static void AddAttributeBool(XmlNode node, string attrName, bool value, bool defaultValue)
        {
            AddAttributeValue<bool>(node, attrName, value, defaultValue);
        }
        #endregion

        #region Vector2

        public static void AddAttributeVector2(XmlNode node, string attrName, Vector2 value)
        {
            AddAttributeVector2(node, attrName, value, Vector2.Zero);
        }
        public static void AddAttributeVector2(XmlNode node, string attrName, Vector2 value, Vector2 defaultValue)
        {
            if (value == defaultValue)
                return;

            AddAttribute(node, attrName, Conversions.Vector2ToString(value));
        }

        #endregion

        #endregion

        #region [SAVING] Storing a List<T>

        public static XmlNode SaveList<T>(IList<T> list) where T: SibLib.Entities.ISaveable
        {
            string name = typeof(T).ToString();
            name = name.Substring(name.LastIndexOf('.') + 1);
            XmlNode ret = Document.CreateElement(name + "s");

            XmlAttribute attr = Document.CreateAttribute("Count");
            attr.Value = list.Count.ToString();
            ret.Attributes.Append(attr);

            foreach (T elem in list)
            {
                elem.SaveState(Document, ret);
            }

            return ret;
        }


        public static List<T> LoadList<T>(XmlNode node) where T: SibLib.Entities.ISaveable, new()
        {
            string name = typeof(T).ToString();
            name = name.Substring(name.LastIndexOf('.') + 1);
            if (node.Name != name + "s")
                throw new InvalidOperationException("Wrong node type in LoadList<" + name + ">");

            int count = GetAttributeInt(node, "Count");
            if (count != node.ChildNodes.Count)
                throw new InvalidOperationException("Wrong child node count in LoadList<" + name + ">");

            List<T> ret = new List<T>(count);
            for (int i = 0; i != count; i++)
            {
                T elem = new T();
                elem.LoadState(node.ChildNodes[i]);

                ret.Add(elem);
            }

            return ret;
        }



        #endregion

        #region [LOADING] Retrieving attributes
        public static T GetAttributeValue<T>(XmlNode node, string attrName, T defaultValue) where T: struct, IConvertible
        {            
            XmlAttribute attr = node.Attributes[attrName];
            return (attr == null) ? defaultValue : (T)Convert.ChangeType(attr.Value, typeof(T), null);          // TODO: must test this, seems almost too smart :)
        }

        public static string GetAttribute(XmlNode node, string attrName)
        {
            XmlAttribute attr = node.Attributes[attrName];
            return (attr == null) ? "" : attr.Value;
        }

        #region Aliases
        public static int GetAttributeInt(XmlNode node, string attrName)
        {
            return GetAttributeInt(node, attrName, 0);
        }        
        public static int GetAttributeInt(XmlNode node, string attrName, int defaultValue)
        {
            string value = GetAttribute(node, attrName);
            return String.IsNullOrEmpty(value) ? defaultValue : int.Parse(value);
        }

        public static float GetAttributeFloat(XmlNode node, string attrName)
        {
            return GetAttributeFloat(node, attrName, 0.0f);
        }
        public static float GetAttributeFloat(XmlNode node, string attrName, float defaultValue)
        {
            string value = GetAttribute(node, attrName);
            return String.IsNullOrEmpty(value) ? defaultValue : float.Parse(value);
        }

        public static bool GetAttributeBool(XmlNode node, string attrName)
        {
            return GetAttributeBool(node, attrName, false);
        }
        public static bool GetAttributeBool(XmlNode node, string attrName, bool defaultValue)
        {
            string value = GetAttribute(node, attrName);
            return String.IsNullOrEmpty(value) ? defaultValue : bool.Parse(value);
        }

        #endregion

        public static Vector2 GetAttributeVector2(XmlNode node, string attrName)
        {
            return GetAttributeVector2(node, attrName, Vector2.Zero);
        }
        public static Vector2 GetAttributeVector2(XmlNode node, string attrName, Vector2 defaultValue)
        {
            XmlAttribute attr = node.Attributes[attrName];
            return (attr == null) ? defaultValue : Conversions.StringToVector2(attr.Value);
        }

        
        #endregion
    }
}